/*
 * uart0-ss805x.c
 *
 * UART0 driver for SS805X
 *
 * Copyright (C) 2024 Sinh Micro, Inc.
 * Subject to the GNU Public License, version 2.
 *
 * Author: lixiang<lixiang@sinhmicro.com>
 * 
 * Encoding format: GB2312
 * Version: v1.2.2
 * Date: 2024-11-05
 */

#include "hal-config.h"
#include "hal-regs.h"
#include "types.h"
#include "system.h"
#include "uart.h"

#if (CONFIG_USING_UART0 == 1 && defined(CONFIG_SYS_CHIP_SERIES_SS805X))

#include "uart0-ss805x-priv.h"

struct uartbuf {
    uint8_t iget;   /* the position get element from */
    uint8_t iput;   /* the position put element to */
    uint8_t num;    /* current elements number */
    uint8_t rsize;  /* ring buffer size */
    uint8_t *rbuf;  /* ring buffer */
};

#if (CONFIG_UART0_USING_IRQ == 1)
static uint8_t          tx_buf[CONFIG_UART0_TX_BUF_LEN];
static uint8_t          rx_buf[CONFIG_UART0_RX_BUF_LEN];
static bool             tx_flag;
static struct uartbuf   txrb, rxrb;
#endif

static void uart_tx_mfp_enable(void)
{
#if (CONFIG_UART0_TX_IO == 0x00)
    MFP3 = (MFP3 & ~(3 << 5)) | (1 << 5);
    MFP0 = MFP0 & 0xfc;      
#elif (CONFIG_UART0_TX_IO == 0x10)
    MFP2 = (MFP2 & 0x7c) | 0x03;
#else
    #error("CONFIG_UART0_TX_IO is invalid!")
#endif
}

static void uart_rx_mfp_enable(void)
{
#if (CONFIG_UART0_USING_RX == 1)
#if (CONFIG_UART0_RX_IO == 0x00)
    MFP3 = (MFP3 & ~(3 << 5)) | (1 << 5);
    MFP0 = (MFP0 & 0xfc) | (1 << 0);
#elif (CONFIG_UART0_RX_IO == 0x10)
    MFP2 = (MFP2 & 0x7c) | 0x02;     
#else
    #error("CONFIG_UART0_RX_IO is invalid!")
#endif
#endif

}

#if (CONFIG_UART0_USING_IRQ == 1)
static void uart_buf_init(struct uartbuf *r, uint8_t *rbuf, uint8_t rsize)
{
    r->iget = r->iput = r->num = 0;

    r->rbuf = rbuf;
    r->rsize = rsize;
}

static int8_t txbuf_put(uint8_t val)
{
    if (txrb.num >= txrb.rsize)
        return -1;  /* full */

    txrb.rbuf[txrb.iput] = val;

    if (txrb.iput == txrb.rsize - 1)
        txrb.iput = 0;
    else
        txrb.iput++;

    txrb.num++;

    return 0;
}

static int8_t rxbuf_put(uint8_t val)
{
    if (rxrb.num >= rxrb.rsize)
        return -1;  /* full */

    rxrb.rbuf[rxrb.iput] = val;

    if (rxrb.iput == rxrb.rsize - 1)
        rxrb.iput = 0;
    else
        rxrb.iput++;

    rxrb.num++;

    return 0;
}

static int8_t txbuf_get(uint8_t *val)
{
    if (txrb.num == 0)
        return -1;  /* empty */

    *val = txrb.rbuf[txrb.iget];

    if (txrb.iget == txrb.rsize - 1)
        txrb.iget = 0;
    else
        txrb.iget++;

    txrb.num--;

    return 0;
}

static int8_t rxbuf_get(uint8_t *val)
{
    if (rxrb.num == 0)
        return -1;  /* empty */

    *val = rxrb.rbuf[rxrb.iget];

    if (rxrb.iget == rxrb.rsize - 1)
        rxrb.iget = 0;
    else
        rxrb.iget++;

    rxrb.num--;

    return 0;
}

static bool txbuf_is_full(void)
{
    return (txrb.num >= txrb.rsize);
}

static bool rxbuf_is_full(void)
{
    return (rxrb.num >= rxrb.rsize);
}

static bool txbuf_is_empty(void)
{
    return (txrb.num == 0);
}

static bool rxbuf_is_empty(void)
{
    return (rxrb.num == 0);
}
#endif

void uart0_init(void)
{  
    uart_rx_mfp_enable();
    uart_tx_mfp_enable();

    SCON = UART0_SCON_VAL;
    SBR = (512 - UART0_BAUD_BR_VAL) & 0xFF;

#if (CONFIG_UART0_USING_IRQ == 1)
    uart_buf_init(&txrb, tx_buf, CONFIG_UART0_TX_BUF_LEN);
    uart_buf_init(&rxrb, rx_buf, CONFIG_UART0_RX_BUF_LEN);

    EUART = 1;
#endif
}

void uart0_turn_rx_on(void)
{
}

void uart0_turn_rx_off(void)
{
}

#if (CONFIG_UART0_USING_IRQ == 0)
void uart0_putc(uint8_t c)
{
    while ((SCON & 0x02) == 0);
    SCON = SCON & ~(1 << 1);
    SBUF = c;
}

bool uart0_is_sending(void)
{
    return ((SCON & 0x02) == 0);
}

int16_t uart0_getc(void)
{
    if ((SCON & 0x01) == 0)
        return -1;

    SCON = SCON & ~(1 << 0);
    return SBUF;
}
#else
void uart0_putc(uint8_t c)
{
    while (txbuf_is_full());

    EUART = 0;
    txbuf_put(c);
    if ((txrb.num >= 1) && !tx_flag)
        SCON |= (1 << 1);
    EUART = 1;
}

bool uart0_is_sending(void)
{
    return tx_flag;
}

int16_t uart0_getc(void)
{
    uint8_t c;

    if (rxbuf_is_empty())
        return -1;

    EUART = 0;
    rxbuf_get(&c);
    EUART = 1;

    return c;
}
#endif

#if (CONFIG_UART0_USING_IRQ == 1)
void uart_ISR(void) INTERRUPT 13
{
    uint8_t c;

    if (SCON & 0x02) {
        SCON &= 0xFD;
        tx_flag = 0;
        if (!txbuf_is_empty()) {
            txbuf_get(&c);
            SBUF = c;
            tx_flag = 1;
        }
    }

    if (SCON & 0x01) {
        c = SBUF;
        rxbuf_put(c);
        SCON &= 0xFE;
    }
}
#endif

#endif
